/*
File: parser.h
Authors: Team 3- Sam Stewart, Dylan McDougall, Stephen Ten Eyck
Date: 06/05/12
CSCE 315- 100 Summer 1 2012
*/

/*
Parser class takes a string using the getString() function,
checks the grammar recursively, and stores a vector of strings.
This vector is accessible to other functions or classes
using getTokens().
*/

#ifndef PARSER_H_
#define PARSER_H_

//includes
#include "std_lib_facilities.h"

/*==========================================================================*/
/*								PARSER CLASS								*/
/*==========================================================================*/

class Parser{
    //private members
    string input;
    int position;														//position of current char in input string
    vector<string> Tokens;												//parsed token stream
    string currToken;													//current token used in parse()
	int exprCount;
    
    //private functions (for checking grammar and obtaining tokens)
    bool getToken(), accept(string s), expect(string s),
			isCommand(), isQuery(), isOpenCmd(), isCloseCmd(), 
			isWriteCmd(), isExitCmd(), isShowCmd(), isCreateCmd(),
			isUpdateCmd(), isInsertCmd(), isDeleteCmd(),
			isRelationName(), isTypedAttributeList(),
			isAttributeList(), isAttributeName(), isAtomicExpr(),
			isCondition(), isComparison(), isConjunction(),
			isOperand(), isOp(), isLiteral(), isExpr(),
			isType(), isInteger(), isIdentifier(),
			alphaCheck(char c), digitCheck(char c),
			isSelection(), isProjection(); //isProduct();
public:

    //constructor
    Parser();
    
    //public functions
    int parse();
    void setString(string);												//sets input string to parse
    vector<string> getTokens();											//returns token stream   
};

/*-----------------------MEMBER FUNCTIONS-----------------------*/

Parser::Parser(){
    position = 0;
    currToken = "";
	exprCount = 0;
}

//handles specific possible input characters as Tokens
bool Parser::getToken(){
	//cout << "got " << currToken << endl;
    currToken = "";
    //used for debugging
	if(position == input.size()){							 
        return false;
    }
	//move the pointer if whitespace is current Token
    if(input.at(position) == ' '){							
        position++;
    }
    //pushes specific input characters onto the vector of strings
	if(input.at(position) == '('){
        currToken = "(";
        Tokens.push_back(currToken);
        position++;
        return true;
    }
    if(input.at(position) == ')'){
        currToken = ")";
        Tokens.push_back(currToken);
        position++;
        return true;
    }
    if(input.at(position) == ';'){
        currToken = ";";
        Tokens.push_back(currToken);
        position++;
        return true;
    }
    if(input.at(position) == ','){
        currToken = ",";
        Tokens.push_back(currToken);
        position++;
        return true;
    }
	if(input.at(position) == '"'){
        currToken = "\"";
		position++;
		while(position != input.size() && input.at(position) != '\"'){
			currToken += input.at(position);
			position++;
		}
		if(position == input.size()){
			error("Expected token: \"\n");
		}
		currToken += input.at(position);
		Tokens.push_back(currToken);
        position++;
        return true;
    }
	//used while loop to build words as Tokens
	//Token ends once certain characters are reached, and is pushed onto the vector
    while(true){
        while(position != input.size() && input.at(position) != ' '
                && input.at(position) != '(' && input.at(position) != ')'
                && input.at(position) != ';' && input.at(position) != ','){
            currToken += input.at(position);
            position++;
        }
        if(position == input.size()){
            Tokens.push_back(currToken);
            return true;
        }
        else if(input.at(position) == ' '){
            Tokens.push_back(currToken);
            position++;
            return true;
        }
        else if(input.at(position) == '('){
            Tokens.push_back(currToken);
            return true;
        }
        else if(input.at(position) == ')'){
            Tokens.push_back(currToken);
            return true;
        }
        else if(input.at(position) == ';'){
            Tokens.push_back(currToken);
            return true;
        }
        else if(input.at(position) == ','){
            Tokens.push_back(currToken);
            return true;
        }    
    }
}

//function used to deviate between what is legal or not as input
bool Parser::accept(string s){								
    if(currToken == s){
        getToken();
        return true;
    }
    return false;
}

//function used to string together valid input command/query expressions
bool Parser::expect(string s){								
    if(accept(s)){
        return true;
    }
	cout << "Expecting: " << s << endl;
    error("Unexpected token");
    return false;
}

//function used for testing whether input command is valid
int Parser::parse(){
	//cout << "parse()\n";
	Tokens.clear();
	exprCount = 0;
	position = 0;
    getToken();
    if(isCommand()){
		if(Tokens[Tokens.size()-1] != ";"){
			getToken();
			if(currToken == ";"){
				cout << "Succesful Command!!!" << endl;
				return 0;
			}
		}
	    cout << "Succesful Command!!!" << endl;
        return 0;
    }
    else if(isQuery()){
		if(Tokens[Tokens.size()-1] != ";"){
			getToken();
			if(currToken == ";"){
				cout << "Succesful Query!!!" << endl;
				return 0;
			}
		}
        cout << "Succesful Query!!!" << endl;
        return 0;
    }
	cout << "Bad Input!!!" << endl;
	Tokens.clear();
	return 1;
}

bool Parser::isCommand(){
	//cout << "isCommand()\n";
    if(isOpenCmd() || isCloseCmd() || isWriteCmd() || isExitCmd()
        || isShowCmd() || isCreateCmd() || isUpdateCmd()
        || isInsertCmd() || isDeleteCmd()){
        return true;
    }
    return false;
}

bool Parser::isOpenCmd(){
	//cout << "isOpenCmd\n";
    if(accept("OPEN")){
        return isRelationName();
    }
    return false;
}

bool Parser::isCloseCmd(){
	//cout << "isCloseCmd\n";
    if(accept("CLOSE")){
        return isRelationName();
    }
    return false;
}

bool Parser::isWriteCmd(){
	//cout << "isWriteCmd()\n";
    if(accept("WRITE")){
        return isRelationName();
    }
    return false;
}

bool Parser::isExitCmd(){
	//cout << "isExitCmd()\n";
    return accept("EXIT");
}

bool Parser::isShowCmd(){
	//cout << "isShowcmd()\n";
    if(accept("SHOW")){
        return isAtomicExpr();
    }
    return false;
}


bool Parser::isCreateCmd(){
	//cout << "isCreateCmd()\n";
    if(accept("CREATE")){
        expect("TABLE");
        if(isRelationName()){
            expect("(");
            if(isTypedAttributeList()){
                expect(")");
                expect("PRIMARY");
                expect("KEY");
                expect("(");
                if(isAttributeList()){
                    expect(")");
                    expect(";");
                    return true;
                }
            }
        }
    }
    return false;
}

bool Parser::isUpdateCmd(){
	//cout << "isUpdateCmd()\n";
    if(accept("UPDATE")){
        if(isRelationName()){
            expect("SET");
            if(isAttributeName()){
                expect("=");
                if(isLiteral()){
                    while(currToken == ","){
                        getToken();
                        if(isAttributeName()){
                            expect("=");
                            if(!isLiteral()){
                                return false;
                            }
                        }
                    }
                    expect("WHERE");
                    return isCondition();
                }
            }
        }
    }
    return false;
}

/*
insert command has two possible input structures
both structures include the same syntax until "VALUES FROM"
first case expects a literal or string of literals which is handled
"else if" is included to separate the other structure
final "else" handles errors in input command
*/
bool Parser::isInsertCmd(){
	//cout << "isInsertCmd()\n";
    if(accept("INSERT")){
        expect("INTO");
        if(isRelationName()){
            expect("VALUES");
            expect("FROM");
            if(currToken == "("){
                getToken();		
                if(isLiteral()){
                    while(currToken == ","){
                        getToken();
                        if(!isLiteral()){
                            break;						
                        }    
                    }
                    expect(")");
                    return true;
                }
            }
            else if(expect("RELATION")){
                return isExpr();
            }
            else{
                return false;
            }
        }
    }
    return false;
}

bool Parser::isDeleteCmd(){
	//cout << "isDeleteCmd()\n";
    if(accept("DELETE")){
        expect("FROM");
        if(isRelationName()){
            expect("WHERE");
            return isCondition();
        }
    }
    return false;
}

bool Parser::isTypedAttributeList(){
	//cout << "isTypedAttributeList()\n";
    if(isAttributeName()){
        if(isType()){
            while(currToken == ","){
                getToken();
                if(isAttributeName()){
                    if(!isType()){
                        return false;
                    }
                }
                else return false;
            }
            return true;
        }
    }
    return false;
}

bool Parser::isType(){
	//cout << "isType()\n";
    if(accept("VARCHAR")){
        expect("(");
        if(isInteger()){
            expect(")");
            return true;
        }
        else return false;
    }
    else{
        return accept("INTEGER");
    }
}

bool Parser::isInteger(){
	//cout << "isInteger()\n";
    istringstream inpStream(currToken);
    double inpValue = 0.0;
    if(inpStream >> inpValue){
        getToken();
        return true;
    }
    else return false;    
}

//only strings and ints
bool Parser::isLiteral(){
	//cout << "isLiteral()\n";
	if(currToken.at(0) == '\"' && currToken.at(currToken.size()-1) == '\"'){
		getToken();
		return true;
	}
	else{
		return isInteger();
	}
}

bool Parser::isAttributeList(){
	//cout << "isAttributeList()\n";
    if(isAttributeName()){
        while(currToken == ","){
            getToken();
            if(!isAttributeName()){
                return false;
            }
        }
        return true;
    }
    return false;
}

bool Parser::isAttributeName(){
	//cout << "isAttributeName()\n";
    return isIdentifier();
}

/*
function first checks to ensure first character is a letter
pending that result ("for loop" begins a second character),
"if statement" ensures the rest of the characters are digits or letters
*/
bool Parser::isIdentifier(){
	//cout << "isIdentifier()\n";
    if(!alphaCheck(currToken.at(0))){
		return false;
    }
    for(int i=1; i<currToken.size(); i++){
        if(alphaCheck(currToken.at(i)) == false && 
			digitCheck(currToken.at(i)) == false){
            return false;
        }
    }
    getToken();
    return true;
}

bool Parser::alphaCheck(char c){
	//cout << "alphaCheck()\n";
    if((int)c < 123 && (int)c > 96){									//ascii chars
        return true;
    }
    else if((int)c < 91 && (int)c > 64){
        return true;
    }
	else if (c == '_'){
		return true;
	}
    else return false;
}

bool Parser::digitCheck(char c){
	//cout << "digitCheck()\n";
    if((int)c < 58 && (int)c > 47){										//ascii chars
        return true;
    }
    else return false;
}

bool Parser::isRelationName(){
	//cout << "isRelationName()\n";
	return isIdentifier();
}

bool Parser::isCondition(){
	//cout << "isCondition()\n";
    return isConjunction();
}

bool Parser::isConjunction(){
	//cout << "isConjunction()\n";
    return isComparison();
}

bool Parser::isComparison(){
	//cout << "isComparrison()\n";
    if(isOperand()){
        if(isOp()){
            if(isOperand()){
                return true;
            }
        }
        return false;
    }
    else if(currToken == "("){
		getToken();
		if(isCondition()){
			expect(")");
			return true;
		}
	}
    return false;
}

bool Parser::isOperand(){
	//cout << "isOperand()\n";
    return(isAttributeName() || isLiteral());
}

bool Parser::isOp(){
	//cout << "isOp()\n";
    if(currToken == "==" || currToken == "!=" || currToken == "<"
        || currToken == ">" || currToken == "<=" || currToken == ">="){
        getToken();
        return true;
    }
    return false;
}

bool Parser::isExpr(){
	//cout << "isExpr()\n";
    if(isSelection()){
		return true;
    }
	else if(isProjection()){
        return true;
    }
	if(isAtomicExpr()){
		return true;
    }
	/*else if(isProduct()){
        return true;
    }
	*/
    return false;
}

/*
This also checks wheather we are given a product or not
by checking if the current token is "*" after obtaining an
expr
*/
bool Parser::isAtomicExpr(){
	exprCount++;
	if(exprCount > 5){
		return false;
	}
	//cout << "isAtomicExpr()\n";
	if(currToken == "("){
		getToken();
		if(isExpr()){
			if(currToken == "*"){		//isProduct()
				getToken();
				return isAtomicExpr();
			}
			expect(")");
			return true;
		}
		else{
			return false;
		}
	}
    if(isRelationName()){
        return true;
    }
    return false;
}

bool Parser::isSelection(){
	//cout << "isSelection()\n";
    if(accept("select")){
        expect("(");
        if(isCondition()){
            expect(")");
            return isAtomicExpr();
        }
    }
    return false;
}

bool Parser::isProjection(){
	//cout << "isProjection()\n";
    if(accept("project")){
        expect("(");
        if(isAttributeList()){
            expect(")");
            return isAtomicExpr();
        }
    }
    return false;
}

/*
isProduct() no longer needed.
It is checked directly in isAtomicExpr().
*/
/*
bool Parser::isProduct(){
	//cout << "isProduct()\n";
    if(isAtomicExpr()){
        expect("*");
        return isAtomicExpr();
    }
    return false;
}
*/

bool Parser::isQuery(){
	//cout << "isQuery()\n";
	if(isRelationName()){
        expect("<-");
        return isExpr();
    }
    return false;
}

/*------------------------------ACCESSOR FUNCTIONS-----------------------*/

void Parser::setString(string str){
    input = str;
}

vector<string> Parser::getTokens(){
    return Tokens;
}

#endif
